package com.gym.common.redis.lock;

import java.util.concurrent.TimeoutException;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import redis.clients.jedis.Jedis;

import com.gym.common.redis.lock.api.IRedisDistributedLockService;
import com.gym.common.redis.lock.bean.LockResponse;
import com.gym.common.redis.resource.JedisCacheUtil;
import com.gym.core.common.UUIDUtils;

/**
 * Redis 分布式锁
 * 
 * @author wangxinchun1988@163.com
 * @date 2015-12-23 下午3:37:23
 */
@Repository
public class RedisDistributedLockService implements
		IRedisDistributedLockService {
	private Logger logger = LoggerFactory
			.getLogger(RedisDistributedLockService.class);

	// 加锁标志
	public static final long ONE_MILLI_NANOS = 1000000L;
	// 默认超时时间（毫秒）
	public static final long DEFAULT_TIME_OUT = 0;

	@Autowired
	private JedisCacheUtil jedisCacheUtil;

	/**
	 * 全局锁服务
	 * 
	 * @param key 唯一key
	 * @param timeout 锁的超时时间，尝试一次会等待10毫秒
	 * @param expireTime 锁的过期时间，过期后会被删除，单位：（秒）
	 * @return LockResponse 锁的结果；唯一标志串
	 */
	public LockResponse lockWithRetry(String key, int tryTimes,
			long tryWaitTimeMills, int expireTime) {
		LockResponse response = LockResponse.errorResponse;
		Jedis jedis = null;
		try {
			jedis = getJedis();
			String uuid = UUIDUtils.getUUID();
			do {
				String nextTime = String.valueOf(System.currentTimeMillis() + expireTime * 1000 + 1) + "_" + uuid;
				Long state = jedis.setnx(key, nextTime);

				if (state == 1
						|| (System.currentTimeMillis() > Long.parseLong(jedis.get(key).split("_")[0]) 
					    && System.currentTimeMillis() > Long.parseLong(jedis.getSet(key, nextTime).split("_")[0]))) {
					jedis.expire(key, expireTime);// 设置锁过期时间
					return new LockResponse(true,uuid);
				} else {
					Thread.sleep(tryWaitTimeMills);
				}
				tryTimes--;
			} while (tryTimes >= 0);

		} catch (Exception e) {
			logger.error("RedisDistributedLock.lock.failed key {}", key);
		} finally {
			releaseJedisInstance(jedis);
		}
		return response;
	}

	public LockResponse lock(String key, int expireTime) {
		return lockWithRetry(key,0,0,expireTime);
	}
	
	/**
	 * 从Jedis池中获取Jedis对象实例
	 * 
	 * @return
	 * @throws TimeoutException
	 */
	private Jedis getJedis() throws TimeoutException {
		Jedis jedis = null;
		jedis = jedisCacheUtil.getJedis();
		return jedis;
	}

	/**
	 * 释放Jedis资源，返回给池
	 * 
	 * @param jedis
	 */
	private void releaseJedisInstance(Jedis jedis) {
		if (jedis != null) {
			jedisCacheUtil.returnJedis(jedis);
		}
	}


	public boolean unlock(String key, String uuid) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			String lockFlag = jedis.get(key);
			if (StringUtils.contains(lockFlag, uuid)) {
				jedis.del(key);
			}
		} catch (Exception e) {
			logger.error("RedisDistributedLock.unlock.failed key {}", key);
			return false;
		} finally {
			releaseJedisInstance(jedis);
		}
		return true;
	}

}
